#include "LecteurPhraseAvecArbre.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER
    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" ||
            ls.getSymCour() == "si" ||
            ls.getSymCour() == "tantque" ||
            ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "lire" ||
            ls.getSymCour() == "ecrire" ||
            ls.getSymCour() == "pour" || 
            ls.getSymCour() == "switch");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <inst_condi>


    if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    }
    else if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
    } else if (ls.getSymCour() == "switch")
        return instSwitch();
    else return 0;
}




////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);

}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    // <instSi> ::=si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> }

    NoeudInstSi* instSi = new NoeudInstSi();

    //si
    sauterSymCour("si");
    sauterSymCour("(");
    instSi->ajouteCondition(expressionBool());
    sauterSymCour(")");
    instSi->ajouteInstruction(seqInst());

    //sinon si
    while (ls.getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        instSi->ajouteCondition(expressionBool());
        sauterSymCour(")");
        instSi->ajouteInstruction(seqInst());
    }

    //sinon
    if (ls.getSymCour() == "sinon") {
        sauterSymCour("sinon");
        instSi->ajouteInstruction(seqInst());
    }
    sauterSymCour("finsi");

    return instSi;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() {
        // <instSwitch> ::= switch (<expression> | <ENTIER>)
        //                                                 case <expression> | <ENTIER>:
        //                                                      <seqInst>
        //                                                 { case <expression> | <ENTIER>:
        //                                                              <seqInst> }
        //                                                 default:
        //                                                      <seqInst>
        //                                                 finswitch

        // definition de sw "globale"
        NoeudInstSwitch* sw;

        // début du switch
        sauterSymCour("switch");
        sauterSymCour("(");
                testerSymCour("<VARIABLE>");
                Noeud* var = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
        sauterSymCour(")");

        sw = new NoeudInstSwitch(var);

        // { case }
        while (ls.getSymCour() == "case") {
                sauterSymCour("case");
                if (ls.getSymCour() == "<ENTIER>") {
                        Noeud* cond = ts.chercheAjoute(ls.getSymCour());
                        sw->ajouteCondition(cond);
                        ls.suivant();
                        
                }
                sauterSymCour(":");
                sw->ajouteInstruction(seqInst()); // on ajoute la sequence d'instruction associée au case
                
                sauterSymCour("break");
        }

        // [ default ]
        if (ls.getSymCour() == "default") {
                ls.suivant();
                sauterSymCour(":");
                sw->ajouteInstruction(seqInst()); // on ajoute la sequence d'instruction par défaut (sinon)
        }
        sauterSymCour("finswitch");
        return sw;

}

///////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instTq() {
    // <insTq> ::= tantque ( <expBool> ) <seqInst> fintantque

    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud * condition = expressionBool();
    sauterSymCour(")");
    Noeud * sequence = seqInst();
    sauterSymCour("fintantque");
    return new NoeudInstTantque(condition, sequence);

}///////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua (<expBool>)

    sauterSymCour("repeter");
    Noeud * sequence = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud * condition = expressionBool();
    sauterSymCour(")");
    return new NoeudInstTantque(sequence, condition);
}

Noeud * LecteurPhraseAvecArbre::instPour() {
    //<instPour> ::= pour (<affectation>;<expressionBool>;<affectation>) <seqInst> finpour

    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud * aff = affectation();
    sauterSymCour(";");
    Noeud * cond = expressionBool();
    sauterSymCour(";");
    Noeud * affec = affectation();
    sauterSymCour(")");
    Noeud * seq = seqInst();
    sauterSymCour("finpour");
    return new NoeudInstPour(aff, cond, affec, seq);

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() {
    // <insLire> ::= lire(<variable>)

    sauterSymCour("lire");
    sauterSymCour("(");
    Noeud* variable = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");
    return new NoeudLire(variable);
}

/////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() {
    // <insEcrire> ::= ecrire(<expression> | <chaine>)

    Noeud* exp;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<CHAINE>") {
        exp = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else {
        exp = expression();
    }

    sauterSymCour(")");
    return new NoeudEcrire(exp);

}


//Expressions arithmétiques et booléennes
///////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier>  |  <variable>  | -  <facteur>  |  ( <expressionBool> )

    Noeud* fact = NULL;

    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-") {
        ls.suivant();
        // on représente le moins unaire ( - facteur) par une soustractin binaire (0  facteur)
        fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expressionBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}

///////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expressionBool() {
    // <expressionBool> ::=  <termeBool> { <termeBool> }

    Noeud* termeB = termeBool();
    while (ls.getSymCour() == "ou") {
        Symbole operateur = opOu();
        Noeud* termeBDroit = termeBool();
        termeB = new NoeudOperateurBinaire(operateur, termeB, termeBDroit);
    }

    return termeB;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::termeBool() {
    // <termeBool> ::= <relation> { <relation> }

    Noeud* relat = relation();
    while (ls.getSymCour() == "et") {
        Symbole operateur = opEt();
        Noeud* relatDroite = relation();
        relat = new NoeudOperateurBinaire(operateur, relat, relatDroite);
    }

    return relat;
}


////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <expression> } 

    Noeud* expre = expression();
    while (ls.getSymCour() == "==" ||
            ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" ||
            ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" ||
            ls.getSymCour() == ">=") {
        Symbole operateur = opRel();
        Noeud* expreDroite = expression();
        expre = new NoeudOperateurBinaire(operateur, expre, expreDroite);
    }
    return expre;
}


////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <facteur> { <opBinaire> <facteur> }

    Noeud* ter = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateur = opBinaire(); // on stocke le symbole de l'opérateur
        Noeud* terDroit = terme(); // lecture de l'operande droit
        ter = new NoeudOperateurBinaire(operateur, ter, terDroit); // const. du noeud
    }
    return ter;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMult><facteur>}

    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole operateur = opMult();
        Noeud* factDroit = facteur();
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
    }
    return fact;
}


//Opérateurs arithmétiques et booléens
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() {
    //<opEt> ::= et

    Symbole operateur;
    if (ls.getSymCour() == "et") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opOu() {
    //<opOu> ::= ou

    Symbole operateur;
    if (ls.getSymCour() == "ou") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOu>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | -  
{
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;

}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
    // <opMult> ::= * | / 

    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=

    Symbole operateur;
    if (ls.getSymCour() == "==" ||
            ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" ||
            ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" ||
            ls.getSymCour() == ">=") {

        operateur = ls.getSymCour();
        ls.suivant();
    } else {
        erreur("<opRel>");
    }
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= non |  -

    Symbole operateur;
    if (ls.getSymCour() == "non" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else {
        erreur("<opUnaire>");
    }
    return operateur;
}


////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBinaire() {
    // <opBinaire> ::= + | - | *  | /
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-" ||
            ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opBinaire>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        cout << endl << " --------Erreur ligne " << ls.getLigne()
                << "  - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        exit(0); // plus tard, on levera une exception
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << " --------Erreur ligne " << ls.getLigne() << "  Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
